package cn.touna.jss.modules.third.tencent.util;

import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.properties.disconf.TencentProperties;
import cn.touna.jss.modules.third.tencent.constant.TencentConstant;
import cn.touna.jss.modules.third.tencent.dto.FaceRecognizeRequest;
import cn.touna.jss.modules.third.tencent.dto.FaceResultReq;
import cn.touna.risk.api.domain.Response;
import cn.touna.risk.api.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.google.common.hash.Hashing;
import org.apache.commons.compress.utils.Charsets;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.*;

/**
 ********************************************************
 * 腾讯人脸识别工具类
 * @author zhangjialian
 * @Date 2018-08-13 19:16
 * @Version V1.0
 ********************************************************/
@Component
public class FaceRecognizeUtil {
    private static final Logger logger = LogManager.getLogger(FaceRecognizeUtil.class);

    @Autowired
    private TencentProperties tencentProperties;
    @Resource(name = "httpsRestTemplate")
    private RestTemplate restTemplate;
    /**
     ********************************************************************
     * 人脸识别生成签名
     * @param values
     * @param ticket
     * @Author  zhangjialian
     * @return  java.lang.String
     * @date    2018/9/10 17:19
     ********************************************************************/
    public static String sign(List<String> values, String ticket) {
        if (values == null) {
            throw new NullPointerException("values is null");
        }
        values.removeAll(Collections.singleton(null));// remove null
        values.add(ticket);
        Collections.sort(values);
        StringBuilder sb = new StringBuilder();
        for (String s : values) {
            sb.append(s);
        }
        return Hashing.sha1().hashString(sb, Charsets.UTF_8).toString().toUpperCase();
    }

    /**
     * 根据字符串生成签名
     * @param context
     * @return
     */
    public static String sign(String context) {
        return Hashing.sha1().hashString(context, Charsets.UTF_8).toString().toUpperCase();
    }
    /**
     ********************************************************************
     * 获取人脸识别access_token
     * @Author  zhangjialian
     * @return  java.lang.String
     * @date    2018/9/10 17:24
     ********************************************************************/
    public String getAccessToken(Response response){
        Map<String,String> urlParams=new HashMap<>();
        urlParams.put("app_id",tencentProperties.getFaceAppId());
        urlParams.put("secret",tencentProperties.getFaceSerectKey());
        urlParams.put("grant_type","client_credential"); //授权类型
        urlParams.put("version", TencentConstant.VERSION);
        String token=null;
        try {
            String resultData=HttpUtil.getInstance().get(tencentProperties.getAccessTokenUrl(),urlParams);
            logger.info("返回腾讯人脸识别token结果:params:{},result:{}",urlParams,resultData);
            if(StringUtils.isNotEmpty(resultData)){
                JSONObject jsonObject=JSON.parseObject(resultData);
                if("0".equals(jsonObject.getString("code"))){//请求成功
                    token = (String)JSONPath.eval(jsonObject, "$.access_token");
                }else{
                    response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),jsonObject.getString("code")+"->"+jsonObject.getString("msg"));
                }
            }
        } catch (Exception e) {
            logger.error("获取人脸识别access_token出现异常,urlParams"+urlParams,e);
            response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),"获取人脸识别access_token出现异常,"+e.getMessage());
        }
        return token;
    }

  /**
   ********************************************************************
   * 根据token和ticket类型获取人脸识别ticket
   * @param token token数据
   * @param ticketType ticket类型:SIGN NONCE
   * @param userId 用户ID
   * @Author  zhangjialian
   * @return  java.lang.String
   * @date    2018/9/11 11:48
   ********************************************************************/
    public String getTicket(String token,String ticketType,String userId,Response response){
        Map<String,String> urlParams=new HashMap<>();
        urlParams.put("app_id",tencentProperties.getFaceAppId());
        urlParams.put("access_token",token);
        urlParams.put("type",ticketType); //授权类型
        urlParams.put("version", TencentConstant.VERSION);
        if(StringUtils.isNotEmpty(userId)){
            urlParams.put("user_id", userId);
        }
        String ticket=null;
        try {
            String resultData=HttpUtil.getInstance().get(tencentProperties.getQueryTicketUrl(),urlParams);
            logger.info("返回腾讯人脸识别ticket结果:params:{},result:{}",urlParams,resultData);
            if(StringUtils.isNotEmpty(resultData)){
                JSONObject jsonObject=JSON.parseObject(resultData);
                if(TencentConstant.FACE_SUCCESS_CODE.equals(jsonObject.getString("code"))){//请求成功
                    ticket = (String)JSONPath.eval(jsonObject, "$.tickets[0].value");
                }else{
                    response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),jsonObject.getString("code")+"->"+jsonObject.getString("msg"));
                }
            }
        } catch (Exception e) {
            logger.error("获取人脸识别ticket出现异常,urlParams="+urlParams,e);
            response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),"获取人脸识别ticket出现异常,"+e.getMessage());
        }
        return ticket;
    }
    /**
     ********************************************************************
     * 合作方后台上送身份信息,并获取h5faceId
     * @param dto 人脸识别请求dto
     * @param h5FaceIdSign 上传身份信息返回的h5FaceId
     * @Author  zhangjialian
     * @return  java.lang.String
     * @date    2018/9/11 11:12
     ********************************************************************/
    public String queryH5faceid(FaceRecognizeRequest dto,String h5FaceIdSign,Response response){
        JSONObject params=new JSONObject();
        params.put("webankAppId",tencentProperties.getFaceAppId());
        params.put("orderNo",dto.getOrderNo());
        params.put("name",dto.getName());
        params.put("idNo",dto.getIdCard());
        params.put("userId",dto.getUserId());
        params.put("sourcePhotoType",dto.getSourcePhotoType());
        params.put("sign",h5FaceIdSign);
        params.put("version", TencentConstant.VERSION);
        String h5faceid=null;
        try {
            String resultData= restTemplate.postForObject(tencentProperties.getQueryH5faceidUrl(),params,String.class);
            logger.info("合作方后台上送身份信息,并获取h5faceId结果:params:{},result:{}",params,resultData);
            JSONObject jsonObject=JSON.parseObject(resultData);
            if(TencentConstant.FACE_SUCCESS_CODE.equals(jsonObject.getString("code"))){//请求成功
                h5faceid = (String)JSONPath.eval(jsonObject, "$.result.h5faceId");
            }else{
                response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),jsonObject.getString("code")+"->"+jsonObject.getString("msg"));
            }
        } catch (Exception e) {
            logger.error("合作方后台上送身份信息,并获取h5faceId结果出现异常,params:"+params,e);
            response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL.getErrorCode(),"合作方后台上送身份信息,并获取h5faceId结果出现异常,"+e.getMessage());

        }
        return h5faceid;
    }
    /**
     ********************************************************************
     * 服务端异步查询人脸识别结果
     * @param req 服务端异步查询请求对象
     * @param sign sign Ticket生成的签名
     * @Author  zhangjialian
     * @return  cn.touna.risk.api.domain.Response
     * @date    2018/9/11 14:45
     ********************************************************************/
    public Response queryFaceResult(FaceResultReq req, String sign){
        Map<String,String> params=new HashMap<>();
        params.put("app_id",tencentProperties.getFaceAppId());
        params.put("version", TencentConstant.VERSION);
        params.put("nonce",req.getNonce());
        params.put("order_no",req.getOrderNo());
        params.put("sign",sign);
        params.put("get_file",req.getGetFile());
        Response response=new Response();
        try {
            String resultData=HttpUtil.getInstance().get(tencentProperties.getQueryFaceResultUrl(),params);
            logger.info("服务端异步查询人脸识别结果:params:{},result:{}",params,resultData);
            JSONObject jsonObject=JSON.parseObject(resultData);
            if(TencentConstant.FACE_SUCCESS_CODE.equals(jsonObject.getString("code"))){//请求成功
                return response.success(jsonObject);
            }else{
                return response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL,jsonObject.getString("code")+"->"+jsonObject.getString("msg"));
            }
        } catch (Exception e) {
            logger.error(TencentConstant.QUERY_ASYNC_FACE_RESULT_EXCEP+",params:"+params,e);
            return response.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL,TencentConstant.QUERY_ASYNC_FACE_RESULT_EXCEP);
        }
    }

    /**
     ********************************************************************
     * 组装上传身份信息sign签名列表
     * @param appId
     * @param orderNo
     * @param name
     * @param idNo
     * @param userId
     * @Author  zhangjialian
     * @return  java.utils.List<java.lang.String>
     * @date    2018/9/10 18:02
     ********************************************************************/
    public static List<String> getSignTicketData(String appId,String orderNo,String name,String idNo,String userId){
        List<String> values=new ArrayList<>();
        values.add(appId);
        values.add(orderNo);
        values.add(name);
        values.add(idNo);
        values.add(userId);
        values.add(TencentConstant.VERSION);
        return values;
    }
   /**
    ********************************************************************
    * 组装人脸识别认证nonce签名lists列表
    * @param appId 腾讯申请appId
    * @param orderNo 订单号
    * @param userId 用户ID
    * @param h5faceId 用户ID
    * @param nonce 随机数：32 位随机串（字母 + 数字组成的随机数）
    * @Author  zhangjialian
    * @return  java.utils.List<java.lang.String>
    * @date    2018/9/10 19:39
    ********************************************************************/
    public static List<String> getNonceTicketData(String appId,String orderNo,String userId, String h5faceId,String nonce){
        List<String> values=new ArrayList<>();
        values.add(appId);
        values.add(orderNo);
        values.add(userId);
        values.add(TencentConstant.VERSION);
        values.add(h5faceId);
        values.add(nonce);
        return values;
    }

    public static List<String> getfaceResultTicketData(String appId,String orderNo,String nonce){
        List<String> values=new ArrayList<>();
        values.add(appId);
        values.add(orderNo);
        values.add(TencentConstant.VERSION);
        values.add(nonce);
        return values;
    }


    /**
     ********************************************************************
     * 组装人脸识别认证url
     * @param dto 人脸识别请求对象
     * @param h5faceId 上传身份信息返回的h5faceId
     * @param nonce 随机数：32 位随机串（字母 + 数字组成的随机数）
     * @param sign nonceTicket生成的签名
     * @Author  zhangjialian
     * @return  java.lang.String
     * @date    2018/9/11 10:56
     ********************************************************************/
    public String generateFaceVertifyUrl(FaceRecognizeRequest dto,String h5faceId, String nonce, String sign){
        Map<String,String> urlParams=new HashMap<>();
        urlParams.put("webankAppId",tencentProperties.getFaceAppId());
        urlParams.put("version",TencentConstant.VERSION);
        urlParams.put("nonce",nonce);
        urlParams.put("orderNo",dto.getOrderNo());
        urlParams.put("h5faceId",h5faceId);
        urlParams.put("url",dto.getCallBackUrl());
        urlParams.put("resultType",null);
        urlParams.put("userId",dto.getUserId());
        urlParams.put("sign",sign);
        urlParams.put("from",dto.getFrom());
        return addParamToUrl(tencentProperties.getVertifyFaceUrl(),urlParams);
    }

    public Response generateFaceResult(FaceRecognizeRequest req,Response response,String h5faceid,String faceVertifyUrl,String signTicket,String nonce){
        JSONObject result=new JSONObject();
        result.put("faceVertifyUrl",faceVertifyUrl);
        result.put("h5faceid",h5faceid);
        result.put("orderNo",req.getOrderNo());
        result.put("nonce",nonce);
        result.put("signTicket",signTicket);
        return response.success(result);
    }

    protected static String addParamToUrl(String url, Map<String, String> urlParams) {
        if(!url.endsWith("?")) {
            url = url + "?";
        }

        ArrayList params = new ArrayList();
        urlParams.entrySet().forEach((entry) -> {
            params.add(new BasicNameValuePair((String)entry.getKey(), (String)entry.getValue()));
        });
        String paramString = URLEncodedUtils.format(params, Charset.forName("UTF-8"));
        url = url + paramString;
        return url;
    }


    /**
     * 检查人脸识别入参
     * @param req
     * @return
     */
    public static Response checkParams(Object req) {
        Field[] fields=req.getClass().getDeclaredFields();
        try {
            for(Field f:fields){
                f.setAccessible(true);
                Object val=f.get(req);
                if(val!=null){
                    if(String.class.isInstance(val)){
                        String str=(String)val;
                        if(StringUtils.isEmpty(str)){
                            return new Response().fail(SystemErrorCodeEnum.JSS_PARAM_CHECK_FAILED.getErrorCode(),
                                    String.format("人脸识别入参,%s字段传值不能为空",f.getName()));
                        }
                    }
                }else{
                    return new Response().fail(SystemErrorCodeEnum.JSS_PARAM_CHECK_FAILED.getErrorCode(),
                            String.format("人脸识别入参%s字段传值不能为空",f.getName()));
                }

            }
        } catch (Exception e) {
            logger.error("人脸识别参数校验异常:",e);
        }
        return null;
    }

}